/*
 * MTF.cpp
 *
 *  Created on: June 9, 2012
 *      Author: Maine
 */

#ifndef MTF_H_
#define MTF_H_

#include "includes.h"
#include "compressedStream.cpp"
#include "MTF.h"

void imprimirVector ( vector<uChar>* v) {
	cout << "VECTOR = [";
	unsigned int j;
	for (j=0; j < v -> size(); j++) {
		if (j != v -> size() -1) cout << (int)v->at(j) << ", ";
		else cout << (int)v->at(j);
			
	}
	cout << "]" << endl;
}

void MTF::setInputFilename(const char* filename){
	this->inputFilename = (char*)filename;
}

void MTF::setOutputFilename(const char* filename){
	this->outputFilename = (char*)filename;
}

/*
 * 
 * 
 * 
 * COMPRESOR
 * 
 * 
 */
 
MTFCompress::MTFCompress() {
		
	static const uChar arr[] = {32,101,116,97,111,110,105,104,115,114,100,108,117,13,10,109,99,119,102,103,121,44,112,98,46,118,34,107,73,45,39,84,65,59,77,72,83,67,120,87,63,66,78,68,69,33,80,113,106,76,70,71,79,82,89,122,86,49,74,48,85,95,58,75,41,40,50,53,51,52,42,91,93,81,96,56,57,54,55,88,47,90,38,124,36,64,35,37,61,125,0,1,2,3,4,5,6,7,8,9,11,12,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,43,60,62,92,94,123,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255};
	this->vectorChars = vector<uChar>(arr, arr + sizeof(arr) / sizeof(arr[0]) );
	
	this->readSize = 0;
	this->lastReadSize = 0;
	this->readingFromFile = false;
	
}
 
MTFCompress::MTFCompress(const char* inputFilename, uInt blocksize=900*1024){
	this -> setInputFilename(inputFilename);	
	
	// Inicializacion del vector predeterminado que propusimos como
	// parte del diseño y como una optimizacion del algoritmo MTF
	
	static const uChar arr[] = {32,101,116,97,111,110,105,104,115,114,100,108,117,13,10,109,99,119,102,103,121,44,112,98,46,118,34,107,73,45,39,84,65,59,77,72,83,67,120,87,63,66,78,68,69,33,80,113,106,76,70,71,79,82,89,122,86,49,74,48,85,95,58,75,41,40,50,53,51,52,42,91,93,81,96,56,57,54,55,88,47,90,38,124,36,64,35,37,61,125,0,1,2,3,4,5,6,7,8,9,11,12,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,43,60,62,92,94,123,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255};
	this->vectorChars = vector<uChar>(arr, arr + sizeof(arr) / sizeof(arr[0]) );
	this->blocksize = blocksize;
	
	this->readSize = 0;
	this->lastReadSize = 0;
	this->imAtFirstBlock  = true;
	this->readingFromFile = true;
	
}

MTFCompress::~MTFCompress(){
	
	if( this->inputFile.is_open() ){
		this->inputFile.close();
	}
	
	if( this->readingFromFile == true ){
		delete this->outputFile;
	}
}

int MTFCompress::getNextBlockOfText(char** text){
	if( ! this->inputFile.is_open() ){
		this->inputFile.open( this->inputFilename , ios::binary );
		if( ! this->inputFile.is_open() ) {
			return ERROR_FILE_NOT_EXIST;
		}
		this->readingFromFile = true;
	}

	if( this->inputFile.eof() ){
		//cout << "Error de lectura." << endl;
		return ERROR_EOF;
	}
	
	//this->inputFile.seekg(this->readSize);
	this->inputFile.read( *text , this->blocksize );
	
	this->lastReadSize =  this->inputFile.gcount();
	
	this->readSize += this->lastReadSize;
	

	if( this->inputFile.bad() ){
		//cout << "Error de lectura." << endl;
		return ERROR_LECTURA;
	}
	
	return 0;
}

int MTFCompress::runNextBlock(vector<uChar>* vector){
	if( this->imAtFirstBlock == true ){
		this->imAtFirstBlock = false;
		this->outputFile = new OCompressedStream( this->outputFilename , this->blocksize );
	}
	
	uInt status;
	char* texto = (char*)malloc( this->blocksize * sizeof(char) );
	
	if (texto != NULL) {
	
		status = this->getNextBlockOfText(&texto);
		
		if( status == ERROR_LECTURA ){
			//cout << "Error de lectura." << endl;
			free(texto);
			return ERROR_LECTURA;

		}
		if( status == ERROR_EOF ){
			//cout << "End of file." << endl;
			free(texto);
			return ERROR_EOF;
		}

		this->runBlock( texto , vector );
				
		this->outputFile->writeMTFResult(vector);
		
		free(texto);
		
		return 0;
	} else {
		return -1;
	}
	
}

int MTFCompress::runBlock(const char* texto, vector<uChar>* vectorR) {
	char* iterador = (char*)texto;
	uChar charIndex;
	uInt cont=0;
	
	vectorR->clear();
	
	if( this->readingFromFile == false ){
		this->lastReadSize = strlen(texto);
	}
	
	while( cont++ < this->lastReadSize ){
		charIndex = (uChar)( std::find(this->vectorChars.begin(), this->vectorChars.end(), (uChar)*iterador ) - this->vectorChars.begin() );
		
		if( charIndex < 0 ) return -1;
		
		// move to front
		this->vectorChars.erase( this->vectorChars.begin() + charIndex );
		this->vectorChars.insert( this->vectorChars.begin() , *iterador );
		
		// Agrego al resultado
		vectorR->push_back(charIndex);
		
		iterador++;
	}
	return 0;
}

/*
int MTFCompress::runBlock(const char* texto, vector<uChar>* vectorR) {
	
	cout << "TEXTO A COMPRIMIR:" << (char*)texto << endl;
	
	//Puntero al vector Y del MTF
	uChar punteroAvector = 0;
	
	//Contador hasta el fin del texto
	unsigned int contador = 0;
	
	if( this->readingFromFile == false ){
		this->lastReadSize = strlen(texto);
	}
	
	while ( contador < this->lastReadSize) {
		// Si el caracter del texto es igual al caracter del vector Y ("vectorChars"),
        // emitimos la posicion del caracter del vector Y,
        // swappeamos caracteres en Y, y movemos el puntero al principio,
        // y nos movemos 1 posicion en el texto
        
		if ( (*(texto + contador)) == vectorChars[punteroAvector]) {

			vectorR -> push_back (punteroAvector);
			
			uChar aux = vectorChars[punteroAvector];
			vectorChars.erase(vectorChars.begin() + punteroAvector);
			
			vector<uChar>::iterator it;
			it = vectorChars.begin();
			it = vectorChars.insert(it, aux);
			
			punteroAvector = 0;
			contador += 1;
			}
			
		// Si la posicion leida no coincide con
		// la del vector de caracteres (Y),
		// nos movemos en el vector Y
		else {
			punteroAvector += 1;
			}
			
		}
		
		return 0;
}
*/

int MTFCompress::run() {
	
	int status = 0;
	vector<uChar>* v = new vector<uChar>;
	while (status != ERROR_EOF && status != ERROR_LECTURA) {
		v->clear();
		status = runNextBlock(v);
	}
	delete(v);
	return 0;
}


/*
 * 
 * 
 * 
 * DESCOMPRESOR
 * 
 * 
 */

MTFDecompress :: MTFDecompress(){
	
	static const uChar arr[] = {32,101,116,97,111,110,105,104,115,114,100,108,117,13,10,109,99,119,102,103,121,44,112,98,46,118,34,107,73,45,39,84,65,59,77,72,83,67,120,87,63,66,78,68,69,33,80,113,106,76,70,71,79,82,89,122,86,49,74,48,85,95,58,75,41,40,50,53,51,52,42,91,93,81,96,56,57,54,55,88,47,90,38,124,36,64,35,37,61,125,0,1,2,3,4,5,6,7,8,9,11,12,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,43,60,62,92,94,123,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255};
	this->vectorChars = vector<uChar>(arr, arr + sizeof(arr) / sizeof(arr[0]) );

	this->readingFromFile = false;
	this->lastReadSize = 0;
	
	this->blocksize = (uInt)(1024 * 900);
}

MTFDecompress :: MTFDecompress(const char* inputFilename){
	this->blocksize = (uInt)(1024 * 900);
	
	this->inputFile = new ICompressedStream( inputFilename , this->blocksize );
	
	this->setInputFilename(inputFilename);
	this->setOutputFilename("MTF.output.txt");
	
	static const uChar arr[] = {32,101,116,97,111,110,105,104,115,114,100,108,117,13,10,109,99,119,102,103,121,44,112,98,46,118,34,107,73,45,39,84,65,59,77,72,83,67,120,87,63,66,78,68,69,33,80,113,106,76,70,71,79,82,89,122,86,49,74,48,85,95,58,75,41,40,50,53,51,52,42,91,93,81,96,56,57,54,55,88,47,90,38,124,36,64,35,37,61,125,0,1,2,3,4,5,6,7,8,9,11,12,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,43,60,62,92,94,123,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255};
	this->vectorChars = vector<uChar>(arr, arr + sizeof(arr) / sizeof(arr[0]) );

	this->readingFromFile = true;
	this->lastReadSize = 0;
}

MTFDecompress::~MTFDecompress(){
	if( this->readingFromFile ){
		delete this->inputFile;
	}

	if( this->outputFile.is_open() ){
		this->outputFile.close();
	}
}

int MTFDecompress::runBlock(const char* textoComprimido, string* textoDescomprimido) {
	char* iterador = (char*)textoComprimido;
	uChar charIndex;
	char aChar;
	uInt cont=0;
	
	textoDescomprimido->clear();
	
	if( this->readingFromFile == false ){
		this->lastReadSize = strlen(textoComprimido) -1;
	}
	
	while( cont++ < this->lastReadSize ){
		charIndex = (uChar)*iterador;
		aChar = (char) this->vectorChars.at( charIndex );
		*textoDescomprimido += aChar;
		
		// move to front
		if( charIndex > 0 ){
			this->vectorChars.erase( this->vectorChars.begin() + charIndex );
			this->vectorChars.insert( this->vectorChars.begin() , (uChar)aChar );
		}
		
		iterador++;
	}
	return 0;
}

/*
int MTFDecompress::runBlock(const vector<uChar>* vectorR, string* texto ){
	uChar punteroAvector = 0;
	
	unsigned int contador = 0;
	
	while ( contador < (vectorR -> size()) ) {
		
		if ((*vectorR)[contador] == punteroAvector) {
			//printf("VECTOR Y: %c, \n ",vectorChars[punteroAvector]);
			texto -> push_back(vectorChars[punteroAvector]);
			
			uChar aux = vectorChars[punteroAvector];
			vectorChars.erase(vectorChars.begin() + punteroAvector);
			
			vector<uChar>::iterator it;
			it = vectorChars.begin();
			it = vectorChars.insert(it, aux);
			
			punteroAvector = 0;
			contador += 1;
			}
			
		// Si la posicion leida no coincide con
		// la del vector de caracteres (Y),
		// nos movemos en el vector Y
		else {
			punteroAvector += 1;
			}
			
		}
		
		return 0;
}
*/

int MTFDecompress::runNextBlock( string* texto ){
	if( ! this->outputFile.is_open() ){
		this->outputFile.open( this->outputFilename , ios::binary );
		if( !this->outputFile.is_open() ) {
			return ERROR_FILE_NOT_EXIST;
		}
	}
	
	bool is_eof = false;
	
	char* resultBlock = new char[this->blocksize];
	memset( resultBlock , '\0' , this->blocksize );
	
	int status = this->inputFile->readMTFresult( &resultBlock );
	
	this->lastReadSize = this->inputFile->lastReadAmount;
	
	if( status == ERROR_EOF ){
		
		if( this->lastReadSize == 0 ){
			delete resultBlock;
			return ERROR_EOF;
		}else{
			// Este caso se da cuando el buffer leido 
			// es menor al tamanio del bloque, y puede pasar solo 1 vez
			// al final del archivo
			is_eof = true;
		}
	}
	
	this->runBlock( resultBlock , texto );
	
	this->outputFile << *texto;
	
	delete[] resultBlock;
	
	if( is_eof ){
		return ERROR_EOF;
	}
	
	return 0;
}

/*
int MTFDecompress::runNextBlock( string* texto ){
	
	if( ! this->outputFile.is_open() ){
		this->outputFile.open( this->outputFilename , ios::binary );
		if( !this->outputFile.is_open() ) {
			return ERROR_FILE_NOT_EXIST;
		}
	}
	
	texto->clear();
	
	vector<uChar>* vectorR = new vector<uChar>;
	
	bool is_eof = false;
	
	if( ERROR_EOF == this->inputFile->readMTFresult( vectorR ) ){
		if( vectorR -> size() == 0 ){
			return ERROR_EOF;
		}else{
			// Este caso se da cuando el buffer leido 
			// es menor al tamanio del bloque, y puede pasar solo 1 vez
			// al final del archivo
			is_eof = true;
		}
	}
	
	this->runBlock(vectorR, texto);
	
	this->outputFile << *texto;
	
	if( is_eof ){
		return ERROR_EOF;
	}
	
	return 0;
}
*/

int MTFDecompress::run(){
	string texto;
	
	while( ERROR_EOF != this->runNextBlock( &texto ) );
	
	return 0;
}
#endif /* MTF_H_ */
